/*
 * @moduleName: InnerTable.js
 * @Desc: table页相关操作
 * @Author: djkloop
 * @Date: 2017-12-11 00:27:04
 * @Last Modified by: djkloop
 * @Last Modified time: 2017-12-15 11:59:57
 */

import React, {Component} from 'react'
import Logger from '../../utils/Logger'
import {Button, Modal, Table, Icon, Affix} from 'antd'
import InnerTableSchemaUtils from './InnerTableSchemaUtils'
import InnerTableRenderUtils from './InnerTableRenderUtils'
import FormSchemaUtils from "../DBForm/InnerFormSchema"
import TableUtil from '../../DBUtils/DBUtils'
import moment from 'moment'
import {connect} from 'react-redux';
import {withRouter} from 'react-router-dom'
import {update, delect} from '../../redux/form.redux'
Logger.getLogger('InnerTable');
@withRouter
@connect(state => state.resMessage, {update, delect})
class InnerTable extends Component {
  state = {
    modalVisible: false, // modal是否可见
    modalTitle: '新增', // modal标题
    modalInsert: true, // 当前modal是用来insert还是update
    selectedRowKeys: [], // 当前有哪些行被选中, 这里只保存key
    // FIXME: 这里可能会有点问题, 父组件中有一个data, 这里又有一个data, 都表示的是表格中的数据, 两边状态不一致, 可能有潜在的bug
    data: [], // 表格中显示的数据

    // 图片预览相关状态
    previewVisible: false, // 是否显示图片预览modal
    previewImages: [], // 要预览的图片
    // 用户自定义组件modal, 一般用于实现单条记录的更新
    componentModalVisible: false,
    cachePage: 1,
    cachePageSize: 10,
    cacheId: null,
    postValue: {},
    resObj: {}
  };
  // 拿到数据初次进入组件的时候
  componentWillMount() {
    // 从远处加载schema
    TableUtil.tryGetSchema(this.props, (res) => {
      // 拿到schema获取啥啥啥... 这个地方不用二次加载了 直接在form里面刷新就好了
      this.setState({
        resObj: TableUtil.updateState(res)
      })
    })
    this.parseTableSchema(this.props);
    this.parseTableData(this.props);
  }

  // 这个分页做的我真是mmp
  handleTableChange = (pagination, filters, sorter) => {
    const pager = {
      ...this.state.pagination
    };
    pager.current = pagination.current;
    pager.size = pagination.pageSize
    let selectorTd = document.querySelectorAll('.indent-level-0');
    let _len = selectorTd.length
    let _id = selectorTd[_len - 1].parentNode.innerText
    this.setState({loading: true, cachePage: pager.current, cachePageSize: pager.size, cacheId: _id});
    if ((this.state.cachePage === pager.current && this.state.cachePageSize !== pager.size) || (pager.current === 1)) {
      this
        .props
        .refresh({
          queryObj: this.state.postValue, page: pagination.current, pageSize: pagination.pageSize,
          // 如果总数超过了总条数 则进行转化
          id: (pager.current === 1)
            ? void 0
            : this.state.cacheId
        })
    } else {
      // 拿到最后一个id 用来优化检索
      this
        .props
        .refresh({queryObj: this.state.postValue, page: pagination.current, pageSize: pagination.pageSize, id: _id})
    }
  }

  parseTableData(props) {
    // 每行数据都必须有个key属性, 如果指定了主键, 就以主键为key 否则直接用个自增数字做key
    const newData = [];
    let i = 0;
    props
      .data
      .data
      .forEach((obj) => {
        // 1
        const newObj = TableUtil.transformRawDataTimeToObject(obj, this.fieldMap);
        if (this.primaryKey) {
          newObj.key = obj[this.primaryKey];
        } else {
          newObj.key = i;
          i++;
        }
        newData.push(newObj);
      });

    // 在这里, 下面两种写法是等效的, 因为parseTableData方法只会被componentWillReceiveProps调用,
    // 而componentWillReceiveProps的下一步就是判断是否re-render 但要注意, 不是任何情况下都等效
    this.setState({
      data: newData,
      postValue: props.postValue.queryObj !== void 0
        ? props.postValue.queryObj
        : {},
      total: props.resMessage.data.count,
      loading: false
    });
  }
  /**
   * 将表格中的一条数据转换为表单中能显示的数据
   */
  transformTableDataToForm(obj) {
    return TableUtil.transformRawTimeToObject(obj.$$rawData);
  }

  parseTableSchema(props) {
    const {tableName, schema} = props;
    const parseResult = InnerTableSchemaUtils.getTableSchema(tableName, schema)
    this.primaryKey = parseResult.primaryKey
    // fieldMap是对原始的dataSchema做了一些处理, 方便查询用的
    this.fieldMap = parseResult.fieldMap;
    const _retTableName = parseResult.tableSchema
    // tableSchema是转换后的Table组件可用的schema 对于tableSchema, 即使命中了缓存, 也要重新设置下render函数
    this.tableSchema = InnerTableRenderUtils.bindRender(_retTableName, tableName, this);
  }
  // 更新数据
  componentWillReceiveProps(nnn) {
    this.parseTableData(nnn)
  }
  // 是否更新
  shouldComponentUpdate(nextProps, nextState) {
    if (this.state.cacheId !== null) {
      if (nextState.cacheId !== this.state.cacheId && nextState.cacheId === null) {}
    }
    return true
  }
  onTableSelectChange = (selectedRowKeys) => {
    this.setState({selectedRowKeys});
  }
  // 删除数据
  onClickDelete = (e) => {
    e.preventDefault();
    let name = void 0;
    this.state.data.forEach(v => v._id === this.state.selectedRowKeys[0] ? name = v.name : null)
    Modal.confirm({
      title: this.state.selectedRowKeys.length > 1 ? '确认批量删除' : '确认删除',
      content: `当前被选中用户: ${name}`,
      // 这里注意要用箭头函数, 否则this不生效
      onOk: () => {
        this.handleDelect()
      },
    });    
  }

  

  // 更新前先转化后台能接收的值
  onUpdateTable = (e) => {
    e.preventDefault();
    // 重置下keysToUpdate, 因为点击表格上方的更新按钮时, 默认是所有字段都可以更新
    this.singleRecordKey = void 0;
    this.keysToUpdate = void 0;
    // 要显示在表单中的值
    const newData = {};
    const multiSelected = this.state.selectedRowKeys.length > 1; // 是否选择了多项
    // 如果只选择了一项, 就把原来的值填到表单里 否则就只把要更新的主键填到表单里
    if (!multiSelected) {
      const selectedKey = this.state.selectedRowKeys[0];
      for (const record of this.state.data) { // 找到被选择的那条记录
        if (record._id === selectedKey) {
          Object.assign(newData, this.transformTableDataToForm(record));
          break;
        }
      }
    } else {
      newData[this.primaryKey] = this
        .state
        .selectedRowKeys
        .join(', ');
    }
    if (multiSelected) {
      this.setState({
        modalVisible: true,
        modalTitle: '批量更新',
        modalInsert: false
      }, () => this.setFormData(newData));
    } else {
      this.setState({
        modalVisible: true,
        modalTitle: '更新',
        modalInsert: false
      }, () => this.setFormData(newData));
    }
  }
  setFormData(data) {
    // 注意这里, 由于antd modal的特殊性, this.formComponent可能是undefined, 要判断一下
    if (this.formComponent) {
      this
        .formComponent
        .props
        .form
        .resetFields();
      if (data) {
        let _resObj = {}
        for (const key in data) {
          // 这个后台发过来的值真鸡儿尴尬
          if (key !== '__v') {
            _resObj[key] = data[key]
          }
        }
        this.formInitData = TableUtil.transformRawTimeToObject(_resObj)
        this
          .formComponent
          .props
          .form
          .setFieldsValue(TableUtil.transformRawTimeToObject(_resObj));
      }
    } else {
      this.formInitData = data;
    }
    this.setState({cacheId: data._id})
  }
  /**
   * 隐藏modal
   */
  hideModal = () => {
    this.setState({modalVisible: false, cacheId: null});
  };

  /**
   *  modal确认 这里才算开始发送ajax
   */
  handleModalOk = (e) => {
    e.preventDefault()
    let validated = true;
    this
      .formComponent
      .props
      .form
      .validateFieldsAndScroll((err, values) => validated = err
        ? false
        : validated); // 不知道有没有更好的办法
    if (!validated) {
      return;
    }
    this.setState({
      cacheValue: this.value,
      selectedRowKeys: [],
      modalVisible: false
    }, () => {
      const newObj = {};
      const oldObj = this
        .formComponent
        .props
        .form
        .getFieldsValue(); // 这里的formComponent必定不是undefined
      for (const key in oldObj) {
        // 只为填坑。
        if (oldObj[key] === undefined || oldObj[key] === null) {
          continue;
        }
        // 跟InnerForm中的filterQueryObj方法很相似
        if (key === this.primaryKey && typeof oldObj[key] === 'string') { // 我在InnerTableSchemaUtils限制死了, modal中的主键字段必定是个string
          newObj[key] = oldObj[key];
        } else if (oldObj[key]instanceof Date) {
          newObj[key] = oldObj[key].format('yyyy-MM-dd HH:mm:ss');
        } else if (moment.isMoment(oldObj[key])) { // 处理moment对象
          newObj[key] = oldObj[key].format('YYYY-MM-DD HH:mm:ss');
        } else if (Array.isArray(oldObj[key])) {
          oldObj[key].forEach((v, index) => {
            if (!moment.isMoment(v) && v.indexOf('-') !== -1) {
              oldObj[key][index] = moment(oldObj[key][index]).format('YYYY-MM-DD HH:mm:ss')
            } else if (moment.isMoment(v)) {
              oldObj[key][index] = moment(oldObj[key][index]).format('YYYY-MM-DD HH:mm:ss')
            }
          })
        } else {
          newObj[key] = oldObj[key];
        }
      }
      this.handleUpdate(newObj)
    })
  }
  /**
 * 远程发个数据
 * @param {*} v
 */
  async handleUpdate(vv) {
    // 把page发送到后台传回来最新的data
    const {pageSize,page} = this.props.postValue;
    const {tableName} = this.props
    vv.pageSize = pageSize
    vv.page = page
    await this
      .props
      .update(tableName, vv)
  }

  /**
   * 删除数据
   */
  async handleDelect(key=this.state.selectedRowKeys[0]) {
    const {tableName, pageSize, page} = this.props;
    await this.props.delect(tableName, {key,pageSize, page})
    this.restKeys()
  }

  /**
   *  重置selectedRowKeys
   */
  restKeys() {
    this.setState({
      selectedRowKeys: []
    })
  }

  render() {
    const {selectedRowKeys} = this.state;
    const {tableName, schema} = this.props.tableName
      ? this.props
      : this.state.resObj;
    const rowSelection = {
      selectedRowKeys: this.state.selectedRowKeys,
      onChange: this.onTableSelectChange
    };

    // 这个地方我觉得要把列表里面的table schema拿到 这样就和上面的检索项对应上了然后在传个特殊的type就能做好区分了
    const hasSelected = selectedRowKeys.length > 0; // 是否选择
    let FormComponent = FormSchemaUtils.getForm(tableName, this.state.resObj.querySchema === void 0
      ? schema
      : this.state.resObj.querySchema, 'single');
    const multiSelected = selectedRowKeys.length > 1; // 是否选择了多项

    return (
      <div>
        {/* 我在想这里要不要把button封装起来?这里的button和form检索项里面的不是一起的呦 */}
        <div className="db-table-btn-container">
          <Affix>
            <Button type="primary">新增</Button>
            <Button type="primary" onClick={this.onUpdateTable} disabled={!hasSelected}>
              <Icon type="edit"/> {multiSelected
                ? '批量修改'
                : '修改'}
            </Button>
            <Button type="primary" disabled={!hasSelected}>查看</Button>
            <Button type="danger" disabled={!hasSelected} onClick={this.onClickDelete}>删除</Button>
          </Affix>
          {/*antd的modal实现中, 如果modal不显示, 那内部的组件是不会mount的, 导致第一次访问this.formComponent会undefined, 而我又需要设置表单的值, 所以新增一个initData属性*/}
          <Modal
            title={this.state.modalTitle}
            visible={this.state.modalVisible}
            onOk={this.handleModalOk}
            onCancel={this.hideModal}
            maskClosable={false}
            width={1000}>
            <FormComponent
              wrappedComponentRef={(input) => {
              this.formComponent = input;
            }}
              initData={this.formInitData}
              cacheId={selectedRowKeys}
              forUpdate={!this.state.modalInsert}
              keysToUpdate={this.keysToUpdate}/>
          </Modal>
        </div>
        <Table
          loading={this.state.loading}
          onChange={this.handleTableChange}
          pagination={{
          pagination: this.state.pagination,
          total: this.state.total,
          showSizeChanger: true,
          pageSizeOptions: [
            '10', '15', '20'
          ],
          size: "small",
          defaultPageSize: 10,
          showQuickJumper: true
        }}
          bordered
          rowSelection={rowSelection}
          dataSource={this.state.data}
          columns={this.tableSchema}/>
      </div>
    )
  }
}

export default InnerTable;
